package com.koron.common;

import java.awt.Color;
import java.awt.Component;
import java.awt.Point;
import java.awt.Window;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.*;
import javax.swing.text.Caret;
import javax.swing.text.JTextComponent;

/**<pre>
 * 代码助手
 * 代码助手同输入框进行绑定通过 {@link #bind(JTextComponent, Window)}进行
 * 当按ctrl+/时，弹出代码自动完成提示
 * 自动完成窗口数据为 {@link ReplacePair},能过 {@link #newPair(String, String)}可以获得
 * 对于代码弹出项固定的，通过 {@link #setList(List)}可以设置选项的数据
 * 对于代码弹出项非固定的，通过{@link #getSyncList()}重载实现。
 * </pre>
 * @author swan
 *
 */
public class MyCodeAssistant extends JDialog{
	/**
	 * 代码填充后，把光标直接定义到此位置
	 */
	public static final String CURSOR = "&CURSOR;";
	
	public static final String SELECT = "$0";
	/**
	 * 绑定的文本录入框
	 */
	private JTextComponent component;
	/**
	 * 弹出的窗体
	 */
	JWindow tipWin = null;
	/**
	 * 代码助手缺省的选项数据
	 */
	private java.util.List<ReplacePair> listData = new ArrayList<>();
	/**
	 * 代码助手里的列表
	 */
	private JList<ReplacePair> list = new JList<>(new DefaultListModel<ReplacePair>());
	/**
	 * 显示数据的选项(包含分组在内)
	 */
	private java.util.List<ReplacePair> rootData = new ArrayList<>();
	/**
	 * 助手所在的组中，再次弹出的时候会自动进入该组
	 */
	private String group;
	/**
	 * 绑定输入框的代码助手
	 * @param component 绑定的输入框
	 * @param window 代码提示框所在的窗体，为null时为独立窗体
	 */
	public void bind(final JTextComponent component,final Window window)
	{
		this.tipWin =new JWindow(window);
		list.setCellRenderer(new InnerListCellRenderer());
		list.setBackground(new Color(255,255,220));
		JScrollPane jsp = new JScrollPane(list);
		tipWin.getContentPane().add(jsp);
		tipWin.setSize(400, 200);
		tipWin.addFocusListener(new FocusListener() {
			@Override
			public void focusLost(FocusEvent e) {
				tipWin.setVisible(false);
			}
			@Override
			public void focusGained(FocusEvent e) {
			}
		});
		list.getInputMap().put(KeyStroke.getKeyStroke((char)KeyEvent.VK_ENTER), "keyenter");
		list.getInputMap().put(KeyStroke.getKeyStroke((char)KeyEvent.VK_ESCAPE), "esc");
		list.getActionMap().put("keyenter", new SelectTip(list));
		list.getActionMap().put("esc",new CloseTip());
		this.component = component;
		ActionMap map = component.getActionMap();
		InputMap im = component.getInputMap();
		im.put(KeyStroke.getKeyStroke('/',InputEvent.ALT_MASK), "tip");
		map.put("tip", new ShowTip());
		map.put("tipesc", new CloseTip());
		map.put("tipup", new TipUpper(list));
		map.put("tipdown", new TipDown(list));
		map.put("tipleft", new TipLeft(list));
		map.put("tipenter", new SelectTip(list));
	}
	/**
	 * 生成提示选项所需要的数据
	 * @param pattern 模板
	 * @param value 值
	 * @param group 所属分组
	 * @return
	 */
	public static ReplacePair newPair(String pattern,String value,String group)
	{
		return new ReplacePair(pattern, value,group);
	}
	/**
	 * 生成提示选项所需要的数据
	 * @param pattern 模板
	 * @param value 值
	 * @return
	 */
	public static ReplacePair newPair(String pattern,String value)
	{
		return new ReplacePair(pattern, value);
	}
	/**
	 * 生成提示选项所需要的数据
	 * @param value 值
	 * @return
	 */
	public static ReplacePair newPair(String value)
	{
		return newPair("^.*$",value);
	}
	
	/**
	 * 设置弹出提示的列表(缺省列表)
	 * @param list 列表
	 */
	public void setStringList(List<String> list)
	{
		((DefaultListModel<ReplacePair>)this.list.getModel()).clear();
		for (String string : list) {
			((DefaultListModel<ReplacePair>)this.list.getModel()).addElement(new ReplacePair("^.*$", string));
		}
	}
	/**
	 * 增加下拉列表的一行数据
	 * @param str 字符串
	 * @see #addList
	 */
	public void addString(String str)
	{
		((DefaultListModel<ReplacePair>)list.getModel()).addElement(new ReplacePair("^.*$",str));
	}
	/**
	 * 设置缺省列表数据
	 * @param list 列表数据
	 */
	public void setList(List<ReplacePair> list)
	{
		this.listData = list;
	}
	
	/**
	 * 增加列表的数据的数据
	 * @param pair 增加的数据
	 */
	public void addPair(ReplacePair pair)
	{
		((DefaultListModel<ReplacePair>)(list.getModel())).addElement(pair);
	}
	/**
	 * 在列表中设置代码助手的选项
	 * @param pairs 选项数据集合
	 */
	public void setPairList(List<ReplacePair> pairs)
	{
		((DefaultListModel<ReplacePair>)this.list.getModel()).clear();
		for (ReplacePair replacePair : pairs) {
			addPair(replacePair);
		}
	}
	/**
	 * 获取提示数据列表。如果为null则使用list。
	 * 否则使用返回缺省的数据作为提示的数据
	 * @return 数据列表
	 */
	public List<ReplacePair> getSyncList()
	{
		return null;
	}
	/**
	 * 处理输入组件中选中的字符经过处理后得到的字符串
	 * @param v 助手列表的数据
	 * @return
	 */
	private String getPairString(ReplacePair v) {
		String disLabel = "";
		if(v.getTarget()!=null)
		{
			String tmpStr = component.getSelectedText();
			if(tmpStr == null) tmpStr = "";
			disLabel = tmpStr.replaceAll(v.getPattern(), v.getTarget());
		}
		return disLabel;
	}
	
	public java.util.List<ReplacePair> getListData() {
		return listData;
	}
	public void setListData(java.util.List<ReplacePair> listData) {
		this.listData = listData;
	}


	/**
	 * 关闭代码助手，并解除弹出窗体的按钮绑定
	 * @author swan
	 *
	 */
	class CloseTip extends AbstractAction
	{
		@Override
		public void actionPerformed(ActionEvent e) {
			component.getInputMap().remove(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE,0));
			component.getInputMap().remove(KeyStroke.getKeyStroke(KeyEvent.VK_UP,0));
			component.getInputMap().remove(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN,0));
			component.getInputMap().remove(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT,0));
			component.getInputMap().remove(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT,0));
			component.getInputMap().remove(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0));
			tipWin.setVisible(false);
		}
	}
	/**
	 * 显示代码助手
	 * @author swan
	 *
	 */
	class ShowTip extends AbstractAction {
		@Override
		public void actionPerformed(ActionEvent e) {
			Point p1 = component.getLocationOnScreen();
			Point p2 = component.getCaret().getMagicCaretPosition();
			if(p2==null)
				p2 = new Point(0,0);
			p1.translate(p2.x, p2.y);
			p1.translate(0,component.getFont().getSize()+3);
			tipWin.setLocation(p1);
			List<ReplacePair> pairs = new ArrayList<>();
			List<ReplacePair> temp = getSyncList();
			if(temp!=null)
				pairs.addAll(temp);
			else if((temp=getListData())!=null)
			{
				pairs.addAll(temp);
			}else
			{
				return;
			}
			for (int i = pairs.size(); i > 0; i--) {
				Pattern p = Pattern.compile(pairs.get(i-1).getPattern());
				String str = component.getSelectedText();
				if(str==null) str = "";
				Matcher m = p.matcher(str);
				if(!m.find())
				{
					pairs.remove(i-1);
				}
			}
			
			List<ReplacePair> parent = new ArrayList<>();
			Map<String,GroupPair> map = new HashMap<>();
			for (ReplacePair replacePair : pairs) {
				if(replacePair.getGroup()==null || replacePair.getGroup().trim().isEmpty())
				{
					parent.add(replacePair);
				}else
				{
					if(map.get(replacePair.getGroup())!=null)
					{
						map.get(replacePair.getGroup()).getList().add(replacePair);
					}else
					{
						GroupPair gp = new GroupPair(replacePair.getGroup());
						gp.getList().add(replacePair);
						parent.add(gp);
						map.put(replacePair.getGroup(), gp);
					}
				}
			}
			rootData = parent;
			if(group == null || group.isEmpty())
				setPairList(parent);
			else
			{
				boolean found = false;
				for (ReplacePair replacePair : parent) {
					if(replacePair instanceof GroupPair)
					{
						if(group.equals(((GroupPair)replacePair).getTarget()))//如果分组等于group,则自动设置数据为分组
						{
							setPairList(((GroupPair)replacePair).getList());
							found = true;
							break;
						}
					}
				}
				if(!found)
					setPairList(parent);
			}
			list.setSelectedIndex(0);
			component.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE,0),"tipesc");
			component.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_UP,0),"tipup");
			component.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN,0),"tipdown");
			component.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT,0),"tipleft");
			component.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT,0),"tipenter");
			component.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0),"tipenter");
			tipWin.setVisible(true);
			component.requestFocus();
		}
	}
	/**
	 * 选择某一项时的动作,完成输入框的内容补齐以及光标设定以及窗体的关闭，键盘事件件的解除
	 * @author swan
	 *
	 */
	class SelectTip extends AbstractAction {
		private JList<ReplacePair> list;
		public SelectTip(JList<ReplacePair> list)
		{
			this.list = list;
		}
		@Override
		public void actionPerformed(ActionEvent e) {
			ReplacePair selectPair = list.getSelectedValue();
			String selectStr = "";
			if(selectPair == null)
			{
				if(list.getModel().getSize()>0)
					selectPair = list.getModel().getElementAt(0);
				else
					return;
			}
			if(selectPair instanceof GroupPair)
			{
				setPairList(((GroupPair)selectPair).getList());
				group = ((GroupPair)selectPair).getTarget();
				return;
			}
			
			selectStr = getPairString(selectPair);
			
			int offset = selectStr.length();
			if(selectStr.indexOf(CURSOR)!=-1)//处理光标问题
			{
				offset = selectStr.indexOf(CURSOR);
				selectStr = selectStr.replaceAll(CURSOR, "");
			}
			Caret caret = component.getCaret();
			StringBuilder sb = new StringBuilder(component.getText());
			int pos = caret.getDot();
			if(component.getSelectedText()!=null)
			{
				pos = component.getSelectionStart();
				sb.replace(component.getSelectionStart(), component.getSelectionEnd(), selectStr);
			}else
				sb.insert(pos, selectStr);
			component.setText(sb.toString());
			caret.setDot(pos + offset);
			tipWin.setVisible(false);
			component.getInputMap().remove(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE,0));
			component.getInputMap().remove(KeyStroke.getKeyStroke(KeyEvent.VK_UP,0));
			component.getInputMap().remove(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN,0));
			component.getInputMap().remove(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT,0));
			component.getInputMap().remove(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT,0));
			component.getInputMap().remove(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0));
		}
	}
	/**
	 * 先择上一项时的动作
	 * @author swan
	 *
	 */
	class TipUpper extends AbstractAction {
		private JList<ReplacePair> list;
		public TipUpper(JList<ReplacePair> list)
		{
			this.list = list;
		}
		@Override
		public void actionPerformed(ActionEvent e) {
			int pos = list.getSelectedIndex();
			if (list.getModel().getSize() > 0) {
				if (pos > 0)
				{
					list.scrollRectToVisible(list.getCellBounds(pos-1,pos-1));
					list.setSelectedIndex(pos - 1);
				}
				else {
					list.scrollRectToVisible(list.getCellBounds(list.getModel().getSize() - 1,list.getModel().getSize()-1));
					list.setSelectedIndex(list.getModel().getSize() - 1);
				}
			}
		}
	}
	/**
	 * 选择下一个数据项时的动作
	 * @author swan
	 *
	 */
	class TipDown extends AbstractAction {
		private JList<ReplacePair> list;
		public TipDown(JList<ReplacePair> list)
		{
			this.list = list;
		}
		@Override
		public void actionPerformed(ActionEvent e) {
			int pos = list.getSelectedIndex();
			if (list.getModel().getSize() > 0) {
				if (pos < list.getModel().getSize() - 1)
				{
					list.scrollRectToVisible(list.getCellBounds(pos+1,pos+1));
					list.setSelectedIndex(pos + 1);
				}
				else
				{
					list.scrollRectToVisible(list.getCellBounds(0,0));
					list.setSelectedIndex(0);
				}
			}
		}
	}
	/**
	 * 按向左键时把选择数据换成rootData的功能
	 * @author swan
	 *
	 */
	class TipLeft extends AbstractAction {
		public TipLeft(JList<ReplacePair> list)
		{
			
		}
		@Override
		public void actionPerformed(ActionEvent e) {
			group = null;
			setPairList(rootData);
		}
	}
	/**
	 * 代码助手里数据选项的泻染
	 * @author swan
	 *
	 */
	class InnerListCellRenderer extends DefaultListCellRenderer
	{
		@Override
		public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
			String disLabel = "";
			if (value instanceof GroupPair) {
				disLabel = ((GroupPair) value).getTarget()+"--->";
			} else {
				ReplacePair v = (ReplacePair) value;
				disLabel = getPairString(v);
				disLabel = disLabel.replaceAll(CURSOR, "");
				if (disLabel.equals(""))
					disLabel = " ";
			}
			Component component =  super.getListCellRendererComponent(list, disLabel, index, isSelected, cellHasFocus);
			if(component instanceof JLabel)
				((JLabel)component).setToolTipText(disLabel);
			return component;
		}
	}
	/**
	 * 代码助手的数据项
	 * @author swan
	 *
	 */
	public static class ReplacePair
	{
		/**
		 * 正则表达式模板
		 */
		private String pattern;
		/**
		 * 目标字符串
		 */
		private String target;
		/**
		 * 分组
		 */
		private String group;
		/**
		 * 构造函数
		 * @param pattern 正则模板
		 * @param target 目标
		 * @param group 所属分组
		 */
		public ReplacePair(String pattern, String target,String group) {
			this.pattern = pattern;
			this.target = target;
			this.group = group;
		}
		/**
		 * 构造函数
		 * @param pattern 正则模板
		 * @param target 目标
		 */
		public ReplacePair(String pattern, String target) {
			this(pattern,target,null);
		}
		
		public String getPattern() {
			return pattern;
		}
		public void setPattern(String pattern) {
			this.pattern = pattern;
		}
		public String getTarget() {
			return target;
		}
		public void setTarget(String target) {
			this.target = target;
		}
		public String getGroup() {
			return group;
		}
		public void setGroup(String group) {
			this.group = group;
		}
	}
	/**
	 * 分组数据项,显示的内容为{@link #target}
	 * @author swan
	 *
	 */
	public static class GroupPair extends ReplacePair
	{
		/**
		 * 所包含的项
		 */
		private List<ReplacePair> list;
		public GroupPair(String capital)
		{
			this(capital,null);
		}
		public GroupPair(String capital,List<ReplacePair> list) {
			super(null,capital);
			if(list!=null)
				this.list = list;
			else
				this.list = new ArrayList<>();
		}
		public List<ReplacePair> getList() {
			return list;
		}
		public void setList(List<ReplacePair> list) {
			this.list = list;
		}
	}
	
	public static void main(String[] args) {
		System.out.println("33".replaceAll("^.*$", "aa$0aa"));
//		JFrame jf = new JFrame();
//		jf.setSize(400,400);
//		JTextArea ta = new JTextArea();
//		jf.add(ta);
//		jf.setVisible(true);
//		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//		MyCodeAssistant myAssistant= new MyCodeAssistant();
//		myAssistant.bind(ta,jf);
//		List<String> list = new ArrayList<String>();
//		list.add("333333333");
//		list.add("11"+MyCodeAssistant.CURSOR+"11");
//		list.add("222"+MyCodeAssistant.SELECT+"2222"+MyCodeAssistant.CURSOR+"22");
//		list.add("12"+MyCodeAssistant.SELECT+"3456"+MyCodeAssistant.CURSOR+"789");
//		ReplacePair pair = new MyCodeAssistant.ReplacePair("(.*)\\s(.*)", "$2    $1");
//		
//		myAssistant.setStringList(list);
//		myAssistant.addPair(pair);
	}
}
